package com.ikun.admin.modules.sys.iservice.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ikun.admin.modules.auth.utils.DefaultPasswordEncoder;
import com.ikun.admin.modules.auth.utils.SecurityUtil;
import com.ikun.admin.modules.sys.entity.*;
import com.ikun.admin.modules.sys.iservice.IAclService;
import com.ikun.admin.modules.sys.service.*;
import com.ikun.admin.modules.sys.tool.excel.UserExcelListener;
import com.ikun.admin.modules.sys.vo.*;
import com.ikun.common.easyexcel.EasyExcelUtil;
import com.ikun.common.exception.MyException;
import com.ikun.common.message.SendEmail;
import com.ikun.common.result.Result;
import com.ikun.common.search.PageData;
import com.ikun.common.search.PageResult;
import com.ikun.common.search.build.QueryBuilder;
import com.ikun.common.search.build.ResultBuilder;
import com.ikun.common.tree.TreeUtil;
import com.ikun.common.utils.DataCopy;
import com.ikun.common.utils.RandomStr;
import com.ikun.common.validate.validator.ValidateExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName IAclServiceImpl
 * @Description:
 * @CreateDate: 2024/5/31 23:55
 */
@Service
public class IAclServiceImpl implements IAclService {

    private static final Integer TYPE_MENU = 0;
    private static final Integer TYPE_PERM = 1;
    private static final Integer IF_SUPER = 0;
    private static final Integer NOT_SUPER = 1;
    private static final Integer NOT_DEV = 1;
    private static final Logger log = LoggerFactory.getLogger(IAclServiceImpl.class);

    @Autowired
    private UserService userService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private RoleMenuService roleMenuService;

    @Autowired
    private SendEmail sendEmail;

    @Autowired
    private DefaultPasswordEncoder defaultPasswordEncoder;

    @Value("${app.configs.development}")
    private Boolean development;

    @Value("${app.configs.sys-name}")
    private String sysName;


    @Override
    public Result<PermMenuVo> selectMenuPerm() {
        Long userId = SecurityUtil.getCurrentUserId();
        User currentUser = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getId, userId));

        List<Menu> menuList = Collections.emptyList();
        if (currentUser.getIfSuper() == IF_SUPER) {
            menuList = menuService.list(new QueryWrapper<Menu>().lambda().eq(!development, Menu::getIfDev, NOT_DEV));
        } else {
            List<UserRole> userRoleList = userRoleService.list(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, userId));
            Set<Long> roleIdSet = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toSet());

            if (roleIdSet.size() > 0) {
                List<RoleMenu> roleMenuList = roleMenuService.list(new QueryWrapper<RoleMenu>().lambda().in(RoleMenu::getRoleId, roleIdSet));
                Set<Long> menuIdSet = roleMenuList.stream().map(RoleMenu::getMenuId).collect(Collectors.toSet());
                if (menuIdSet.size() > 0) {
                    menuList = menuService.list(new QueryWrapper<Menu>().lambda().in(Menu::getId, menuIdSet).eq(!development, Menu::getIfDev, NOT_DEV));

                }
            }
        }

        PermMenuVo permMenuVo = new PermMenuVo();
        if (menuList.size() > 0) {

            List<Menu> menus = menuList.stream().filter(item -> Objects.equals(item.getType(), TYPE_MENU)).collect(Collectors.toList());
            List<Menu> routes = menus.stream().filter(item -> (Objects.nonNull(item.getRouter())) && !Objects.equals(item.getRouter(), "")).collect(Collectors.toList());
            Map<Long, List<Menu>> menuPermMap = menuList.stream().filter(item -> Objects.equals(item.getType(), TYPE_PERM)).collect(Collectors.groupingBy(Menu::getPid));

            List<PermMenuVo.MenuVo> menuVoList = DataCopy.copyToListObj(menus, PermMenuVo.MenuVo.class);
            menuVoList.stream().forEach(item -> {
                if (Objects.nonNull(item.getRouter()) && !Objects.equals(item.getRouter(), "")) {
                    Long id = item.getId();
                    List<String> permList = menuPermMap.get(id).stream().map(menu -> menu.getPerm()).collect(Collectors.toList());
                    item.setPermList(permList);
                }

            });

            List<PermMenuVo.MenuVo> menuVoTreeList = TreeUtil.build(menuVoList);

            List<PermMenuVo.MenuVo> routerList = DataCopy.copyToListObj(routes, PermMenuVo.MenuVo.class);
            permMenuVo.setMenuList(menuVoTreeList);
            permMenuVo.setRouterList(routerList);

        }
        return new Result<PermMenuVo>().success(permMenuVo);
    }

    /**
     * 查询用户角色集合
     * @param id
     * @return
     */
    @Override
    public List<RoleCommonVo> selectRoleByUser(Long id) {
        List<UserRole> userRoleList = userRoleService.list(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, id));
        Set<Long> roleIdSet = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toSet());
        List<Role> roleList = new ArrayList<>();
        if (roleIdSet.size() > 0) {
            roleList = roleService.list(new QueryWrapper<Role>().lambda().in(Role::getId, roleIdSet));
        }
        List<RoleCommonVo> roleCommonVolist = DataCopy.copyToListObj(roleList, RoleCommonVo.class);

        return roleCommonVolist;


    }

    /**
     * 查询用户权限值集合
     * @param id
     * @return
     */
    @Override
    public List<String> selectMenuByUser(Long id) {
        List<UserRole> userRoleList = userRoleService.list(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, id));
        Set<Long> roleIdSet = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toSet());

        List<RoleMenu> roleMenuList = new ArrayList<>();
        if (roleIdSet.size() > 0) {
            roleMenuList = roleMenuService.list(new QueryWrapper<RoleMenu>().lambda().in(RoleMenu::getRoleId, roleIdSet));
        }
        List<Menu> menuList = new ArrayList<>();
        if (roleMenuList.size() > 0) {
            Set<Long> menuIdSet = roleMenuList.stream().map(RoleMenu::getMenuId).collect(Collectors.toSet());
            menuList = menuService.list(new QueryWrapper<Menu>().lambda().in(Menu::getId, menuIdSet).eq(Menu::getType, TYPE_PERM).select(Menu::getPerm));
        }
        return menuList.stream().filter(item -> Objects.nonNull(item.getPerm())).map(Menu::getPerm).collect(Collectors.toList());
    }

    @Override
    public Result<PageResult<UserListResultVo>> userList(PageData<UserListParamVo> pageData) {
        UserListParamVo userListParamVo = pageData.getCondition();
        Page<User> userPage = new Page<User>().setCurrent(pageData.getPageParam().getCurrent()).setSize(pageData.getPageParam().getSize());

        QueryWrapper<User> userQueryWrapper = new QueryWrapper<User>();
        Boolean build = QueryBuilder.build(userQueryWrapper, userListParamVo);
        if (!build) {
            return new Result<PageResult<UserListResultVo>>().success(PageResult.emptyPageResult());
        }
        userService.page(userPage, userQueryWrapper);
        if (userPage.getRecords().size() == 0) {
            return new Result<PageResult<UserListResultVo>>().success(PageResult.emptyPageResult());
        }
        List<UserListResultVo> userListResultVoList = DataCopy.copyToListObj(userPage.getRecords(), UserListResultVo.class);
        Set<Long> userIdSet = null;
        if (userListResultVoList.size() > 0) {
            userIdSet = userListResultVoList.stream().map(UserListResultVo::getId).collect(Collectors.toSet());
        }
        List<UserRole> userRoleList = userRoleService.list(new QueryWrapper<UserRole>().lambda().in(UserRole::getUserId, userIdSet));
        Map<Long, List<Long>> userRoleMap = userRoleList.stream()
                .collect(Collectors.groupingBy(
                        UserRole::getUserId,
                        Collectors.mapping(UserRole::getRoleId, Collectors.toList())
                ));
        userListResultVoList.stream().forEach(item -> Optional.ofNullable(userRoleMap.get(item.getId())).ifPresent(userRoles -> item.setRoleIdList(userRoles)));
        return new Result<PageResult<UserListResultVo>>().success(PageResult.getInstance(userPage.getTotal(), userListResultVoList));

    }

    @Override
    public Result<UserDetailVo> userDetail(Long id) {
        User user = userService.getById(id);
        UserDetailVo userDetailVo = DataCopy.copyToObj(user, UserDetailVo.class);
        if (Objects.equals(user.getIfSuper(), 0)) {
            List<Role> list = roleService.list(new QueryWrapper<>());
            List<Long> roleIdList = list.stream().map(Role::getId).collect(Collectors.toList());
            userDetailVo.setRoleIdList(roleIdList);
        } else {
            List<UserRole> userRoleList = userRoleService.list(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, id).select(UserRole::getRoleId));
            if (userRoleList.size() > 0) {
                List<Long> roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList());
                userDetailVo.setRoleIdList(roleIdList);
            }
        }
        return new Result<UserDetailVo>().success(userDetailVo);
    }

    @Override
    public Result<List<RoleDictVo>> roleDict() {
        List<Role> roleList = roleService.list(new QueryWrapper<Role>());
        List<RoleDictVo> resultList = new ArrayList<>();
        if (roleList.size() > 0) {
            resultList = DataCopy.copyToListObj(roleList.stream().filter(item -> item.getRoot() == 1).collect(Collectors.toList()), RoleDictVo.class);
        }
        return new Result<List<RoleDictVo>>().success(resultList);
    }

    @Override
    public Result<PageResult<RoleDictVo>> roleList(PageData<RoleListParamVo> pageData) {
        RoleListParamVo condition = pageData.getCondition();
        PageData.PageParam pageParam = pageData.getPageParam();
        Page rolePage = new Page();

        rolePage.setCurrent(pageParam.getCurrent());
        rolePage.setSize(pageParam.getSize());
        QueryWrapper<Role> roleQueryWrapper = new QueryWrapper<>();

        Boolean build = QueryBuilder.build(roleQueryWrapper, condition);
        if (!build) {
            return new Result<PageResult<RoleDictVo>>().success(PageResult.emptyPageResult());

        }
        roleService.page(rolePage, roleQueryWrapper);
        List<RoleDictVo> resultList = new ArrayList<>();
        if (rolePage.getRecords().size() > 0) {
            ResultBuilder.buildResult(rolePage.getRecords(), resultList, RoleDictVo.class);
        }
        return new Result<PageResult<RoleDictVo>>().success(PageResult.getInstance(rolePage.getTotal(), resultList));
    }

    @Override
    @Transactional
    public Result<String> addOrUpdateUser(UserAddOrUpdateVo userAddOrUpdateVo) {
        ValidateExecutor.execute(userAddOrUpdateVo, null);
        //校验手机号和邮箱是否被注册
        if (userService.getOne(new QueryWrapper<User>().lambda().eq(User::getMobile, userAddOrUpdateVo.getMobile()).ne(Objects.nonNull(userAddOrUpdateVo.getId()), User::getId, userAddOrUpdateVo.getId())) != null) {
            throw new MyException("手机号已经被注册啦");
        }
        if (userService.getOne(new QueryWrapper<User>().lambda().eq(User::getEmail, userAddOrUpdateVo.getEmail()).ne(Objects.nonNull(userAddOrUpdateVo.getId()), User::getId, userAddOrUpdateVo.getId())) != null) {
            throw new MyException("邮箱已经被注册啦");
        }
        User user = DataCopy.copyToObj(userAddOrUpdateVo, User.class);
        user.setId(Objects.isNull(user.getId()) ? IdUtil.getSnowflakeNextId() : user.getId());
        List<UserRole> userRoleList = userAddOrUpdateVo.getRoleIdList().stream().map(item -> {
            UserRole userRole = new UserRole();
            userRole.setUserId(user.getId());
            userRole.setRoleId(item);
            return userRole;
        }).collect(Collectors.toList());
        if (Objects.isNull(userAddOrUpdateVo.getId())) {
            //新增

            String username = userAddOrUpdateVo.getMobile();
            String password = RandomStr.generateVerificationCode(10, true);
            user.setUsername(username);
            user.setPassword(defaultPasswordEncoder.encode(password));
            userService.save(user);
            userRoleService.saveBatch(userRoleList);
            //邮件通知
            sendEmail.sendTextEmail(user.getEmail(), String.format(SendEmail.REGISTER_EMAIL_TEMPLATE_SUBJECT, user.getRealName(), sysName),
                    String.format(SendEmail.REGISTER_EMAIL_TEMPLATE_CONTENT, username, password));

        } else {
            //编辑
            userService.update(new UpdateWrapper<User>().lambda().eq(User::getId, userAddOrUpdateVo.getId())
                    .set(Objects.nonNull(userAddOrUpdateVo.getNickname()), User::getNickname, userAddOrUpdateVo.getNickname())
                    .set(Objects.nonNull(userAddOrUpdateVo.getHeadUrl()), User::getHeadUrl, userAddOrUpdateVo.getHeadUrl()));
            userRoleService.remove(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, userAddOrUpdateVo.getId()));
            userRoleService.saveBatch(userRoleList);
        }

        return new Result<String>().success("操作成功");
    }

    @Override
    @Transactional
    public Result<String> deleteUser(Long id) {

        userRoleService.remove(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, id));
        userService.removeById(id);
        return new Result<String>().success("操作成功");
    }

    @Override
    public Result<RoleDictVo> roelDetail(Long id) {
        Role role = roleService.getById(id);
        RoleDictVo roleDictVo = DataCopy.copyToObj(role, RoleDictVo.class);
        return new Result<RoleDictVo>().success(roleDictVo);
    }

    @Override
    @Transactional
    public Result<String> addOrUpdateRole(RoleDictVo roleDictVo) {
        ValidateExecutor.execute(roleDictVo, null);
        Role role = DataCopy.copyToObj(roleDictVo, Role.class);
        if (Objects.isNull(role.getId())) {
            roleService.save(role);
        } else {
            roleService.update(new UpdateWrapper<Role>().lambda().eq(Role::getId, role.getId())
                    .set(Role::getRemark, role.getRemark()));
        }
        return new Result<String>().success("操作成功");
    }

    @Override
    @Transactional
    public Result<String> deleteRole(Long id) {
        //查询当前角色下是否有用户
        List<UserRole> userRoleList = userRoleService.list(new QueryWrapper<UserRole>().lambda().eq(UserRole::getRoleId, id));
        if (userRoleList.size() > 0) {
            throw new MyException("当前角色下还有" + userRoleList.size() + "位用户,请先删除当前角色下的用户");
        }
        roleService.removeById(id);
        return new Result<String>().success();
    }

    @Override
    public Result<List<PermTreeNodeVo>> permTree() {
        List<Menu> menuList = menuService.list(new QueryWrapper<>());
        List<PermTreeNodeVo> permTreeNodeVoList = DataCopy.copyToListObj(menuList, PermTreeNodeVo.class);
        List<PermTreeNodeVo> resultTreeList = new ArrayList();
        if (permTreeNodeVoList.size() > 0) {
            resultTreeList = TreeUtil.build(permTreeNodeVoList);
            resultTreeList.stream().sorted(Comparator.comparing(PermTreeNodeVo::getSort)).collect(Collectors.toList());
        }
        return new Result<List<PermTreeNodeVo>>().success(resultTreeList);
    }

    @Override
    public Result<List<Long>> selectMenuByRoleId(Long id) {

        List<RoleMenu> menuRoleList = roleMenuService.list(new QueryWrapper<RoleMenu>().lambda().eq(RoleMenu::getRoleId, id));
        List<Long> resultList = new ArrayList<>();
        if (menuRoleList.size() > 0) {
            resultList = menuRoleList.stream().map(RoleMenu::getMenuId).collect(Collectors.toList());
        }
        return new Result<List<Long>>().success(resultList);
    }

    @Override
    @Transactional
    public Result<String> addOrUpdateRoleMenu(RoleMenuAddOrUpdateVo roleMenuAddOrUpdateVo) {
        ValidateExecutor.execute(roleMenuAddOrUpdateVo, null);
        roleMenuService.remove(new QueryWrapper<RoleMenu>().lambda().eq(RoleMenu::getRoleId, roleMenuAddOrUpdateVo.getRoleId()));
        List<RoleMenu> roleMenuList = roleMenuAddOrUpdateVo.getPermIdList().stream().map(permId -> {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setRoleId(roleMenuAddOrUpdateVo.getRoleId());
            roleMenu.setMenuId(permId);
            return roleMenu;
        }).collect(Collectors.toList());
        roleMenuService.saveBatch(roleMenuList);
        return new Result<String>().success("操作成功");
    }

    @Override
    public Result<List<UserDictVo>> selectUserDict() {
        Long currentUserId = SecurityUtil.getCurrentUserId();
        List<User> userList = userService.list(new QueryWrapper<User>().lambda().ne(User::getId, currentUserId).ne(User::getIfSuper, IF_SUPER));
        List<UserDictVo> userDictVoList = DataCopy.copyToListObj(userList, UserDictVo.class);
        return new Result<List<UserDictVo>>().success(userDictVoList);
    }

    @Override
    public void exportUser(HttpServletResponse response, PageData<UserListParamVo> pageData) {
        UserListParamVo condition = pageData.getCondition();

        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();

        Boolean build = QueryBuilder.build(userQueryWrapper, condition);
        if (!build) {
            throw new MyException("查询数据为空,导出失败");
        }
        userQueryWrapper.lambda().orderByDesc(User::getCreateTime);
        List<User> userList = userService.list(userQueryWrapper);
        if (userList.size() == 0) {
            throw new MyException("查询数据为空,导出失败");
        }
        List<UserExportAndImportVo> userExportAndImportVoList = DataCopy.copyToListObj(userList, UserExportAndImportVo.class);

        try {
            EasyExcelUtil.exportSingleSheetExcel(response, "用户信息表", UserExportAndImportVo.class, "sheet1", userExportAndImportVoList);
        } catch (IOException e) {
            log.error("导出失败{}", e);
            throw new MyException("导出失败");
        }
    }

    @Override
    public Result<String> importUser(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), UserExportAndImportVo.class, new UserExcelListener(userService, defaultPasswordEncoder)).sheet().doRead();
        } catch (IOException e) {
            throw new MyException("导入失败");
        }
        return new Result<String>().success("操作成功");
    }
}
